在設計元件時使用 slot
,可以為元件設計帶來很高的彈性。
並減少過度拆元件造成的傳遞參數問題,也就是 props drilling ,
常見使用方法有 <slot>
、useSlots()
,以及我們會帶到 slot
的運作原理。
<slot>
不具名插槽slot
顧名思義就是一個插槽的意思,
你把要嵌入的組件或文字放入設計好的元件中。
那 <slot></slot>
將會被替換成你所放入的東西,
也就是說 <slot>
是起到一個佔位符的作用。
<FancyButton>
Click me! <!-- 這是你要塞入的文字或組件 -->
</FancyButton>
▲父組件
<button>
<slot></slot>
</button>
▲子組件
上面的程式碼中只提供了一個 <slot></slot>
插槽,
那如果我今天要有多個插槽呢?
譬如下面這樣 :
<div class="container">
<header>
<!-- 組件的 header 位置 -->
</header>
<main>
<!-- 組件的 main 位置 -->
</main>
<footer>
<!-- 組件的 footer 位置 -->
</footer>
</div>
▲元件內尚未使用插槽
這個時候具名插槽就很好用了, <slot>
元素有一個特殊的 attribute name
, 作用類似於插槽的唯一 ID。
<div class="container">
<header>
<slot name="header"></slot>
</header>
<main>
<slot></slot> <!-- 這邊沒有給名字,所以預設名字是 "default" -->
</main>
<footer>
<slot name="footer"></slot>
</footer>
</div>
▲元件內使用具名插槽和未具名插槽
<BaseLayout>
<template v-slot:header>
<!-- content for the header slot -->
</template>
</BaseLayout>
▲父組件使用有具名插槽的元件
通常情況下 沒有 name的,會被隱式的命名為 default,
透過不具名 傳入的組件會被預設放在 default 插槽裡
這邊其實官網的說明還蠻 "簡潔" 的
Returns the slots object from the Setup Context, which includes parent passed slots as callable functions that return Virtual DOM nodes.
簡單來說就是回傳 slots
物件讓你可以做一些額外操作,
例如說下面這個情境:
<script setup>
import { useSlots } from 'vue'
const slot = useSlots()
const hasHeader = slot.header;
const hasFooter = slot.footer;
</script>
<template>
<div class="container">
<div v-if="hasHeader" class="mb-1">
<slot name="header"></slot>
</div>
<div v-if="hasFooter" class="mb-1">
<slot name="footer"></slot>
</div>
</div>
</template>
如果沒有某個欄位的話,你不會希望他有莫名其妙的 margin,
如果這邊沒做這個操作,你可以會覺得說:
喔 ~ 我不傳 slot 那應該就不會 render 了吧。
但外面那層 div 還是存在鴨。
slot
運作原理slot
是一個我覺得非常直觀的使用方法,因為你放什麼他就會出現什麼,
但他背後是怎麼 work 的呢?
<!-- App.vue-->
<template>
<section>
<Comp>
<h1>鐵人賽</h1>
</Comp>
</section>
</template>
<!-- Comp.vue-->
<template>
<div class="container"></div>
</template>
各位覺得這個會出現什麼呢?
沒錯 nothing
但為什麼我們放個 <slot>
就可以把這些 HTML 傳進去,
我們來看一下編譯後的渲染函數是長怎樣吧。
<template>
<div class="container">
<slot ></slot>
</div>
</template>
▲Comp.vue 編譯前
// 略
import { renderSlot as _renderSlot, openBlock as _openBlock, createElementBlock as _createElementBlock } from "vue"
// 略
function render(_ctx, _cache, $props, $setup, $data, $options) {
return (_openBlock(), _createElementBlock("div", _hoisted_1, [
_renderSlot(_ctx.$slots, "default")
// slot 標籤在這裡被編譯成插槽函數
]))
}
// 略
▲Comp.vue 編譯後
function renderSlot(slots, name, props = {}, fallback, noSlotted) {
if (/*略...*/) {
if (name !== "default") props.name = name; // 具名插槽替換 default
// 回傳 virtual DOM 相關工具函數
return openBlock(), createBlock(
Fragment,
null,
[createVNode("slot", props, fallback && fallback())],
64
);
}
// 略...
▲renderSlot 原始碼
一路從模板語法 <template>
編譯成渲染函數,
再根據渲染函數中的插槽函數了解原始碼運作原理,
所以一句話總結, slot
並不是什麼魔法,就只是又包了一層插槽函數。
其實原本以為自己已經都知道 slot
怎麼寫,
不過認真翻過文章後還是發現一些自己沒注意到的用法。
今天我們從基礎的 slot
用法,具名插槽的用法,useSlots()
來協助我們做一些條件判斷操作,
以及最後的插槽運作原理程式碼。
今天是我們組件溝通篇章的最後一篇,明天將進入更進階的套件使用環節,
相信經過這段時間你應該已經能了解最基本的使用方法,甚至能根據情境判斷該用何種 API,
那真實的專案並不只會靠自己純手刻,因為時間限制的原因,
使用別人造好的輪子當然能節省我們很多困擾。
跟前面的文章一樣,你將會學習到不局限於 Vue 的觀念及實作。
如果你喜歡這個系列或是想看我發瘋,歡迎按下 訂閱
一起走完這三十天吧。
slot
slot
? 什麼情況用 props
就能解決了useSlot
可以用在什麼情況?